#!/usr/bin/env python
#coding=utf-8

import json
import time
from pprint import pprint, pformat
from aliyunsdkcore.acs_exception.exceptions import ClientException
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkslb.request.v20140515.DescribeAccessControlListsRequest import DescribeAccessControlListsRequest
from aliyunsdkslb.request.v20140515.DescribeAccessControlListAttributeRequest import DescribeAccessControlListAttributeRequest
from aliyunsdkslb.request.v20140515.AddAccessControlListEntryRequest import AddAccessControlListEntryRequest
from aliyunsdkslb.request.v20140515.RemoveAccessControlListEntryRequest import RemoveAccessControlListEntryRequest
from core.logger import logger
import pandas as pd
from common.aliyun.aliyun_client import AliYunClient

class AliyunSlbAcl(object):
    def __init__(self):
        self.aliyun_client = AliYunClient()
        self.aliyun = self.aliyun_client.get_all_client()


    def get_all_acl_id(self, account):
        """
        获取访问控制组的id信息
        :param acl_name:
        :return:
        """
        page_size = 50
        page_number = 1
        all_acl_info = []

        while True:
            request = DescribeAccessControlListsRequest()
            request.set_accept_format('json')
            request.set_PageNumber(page_number)
            request.set_PageSize(page_size)

            try:
                response = account["client"].do_action_with_exception(request)
                response = json.loads(str(response, encoding='utf-8'))

                for acl_tmp in response["Acls"]["Acl"]:
                    acl_info = {
                        "account": account["name"],
                        "id": acl_tmp['AclId'],
                        "name": acl_tmp["AclName"],
                    }
                    all_acl_info.append(acl_info)

                if page_number * page_size >= response["TotalCount"]:
                    break
                else:
                    page_number += 1

            except Exception as e:
                logger.error("{} get all acl id error =>{}".format(account["name"], e))

        return all_acl_info

    def get_acl_by_name(self, acl_name):
        acls_info = self.get_all_acl_info()
        for acl in acls_info:
            if acl["name"] == "acl_name":
                return acl

        return None



    def get_all_acl_info(self):
        acls_info = []
        for account in self.aliyun:
            # 先拿到每个访问控制的id信息
            acl_info = self.get_all_acl_id(account)
            if not acl_info:
                logger.error("get acl id failed =>{}".format(account["name"]))
                continue

            # 根据访问控制的id来获取白名单列表
            result = self.get_acl_entry(acl_info)
            if not result:
                logger.error("get acl entry failed =>{}".format(account["name"]))
                continue

            acls_info.extend(acl_info)

        logger.info("get all acl info success")
        return acls_info


    def get_acl_entry(self, acl_info):
        """
        获取每个访问控制组的白名单信息
        :param acl_info:
        :return:
        """
        for acl in acl_info:
            try:
                account = self.aliyun_client.get_one_client(acl["account"])
                request = DescribeAccessControlListAttributeRequest()
                request.set_accept_format('json')
                request.set_AclId(acl["id"])

                response = account["client"].do_action_with_exception(request)
                response = json.loads(str(response, encoding='utf-8'))
                if "RelatedListeners" in response and len(response['RelatedListeners']['RelatedListener']) == 0:
                    print(f"{acl['account']} -> {acl['name']}")
                    continue
                acl["acl_entrys"] = response["AclEntrys"]["AclEntry"]
            except Exception as e:
                logger.error("{} get acl entry error {}=>{}".format(acl["account"], acl["name"], e))
                return False

        return True


    def add_acl_entrys(self, acl, acl_entrys):
        """
        给访问控制增加白名单列表
        """
        if not acl_entrys:
            return True

        ali_entrys = []
        for entry, comment in acl_entrys.items():
            acl_entry = {
                "entry": entry,
                "comment": comment
            }
            ali_entrys.append(acl_entry)
        ali_entrys = json.dumps(ali_entrys)

        try:
            account = self.aliyun_client.get_one_client(acl["account"])
            request = AddAccessControlListEntryRequest()
            request.set_accept_format('json')
            request.set_AclId(acl["id"])
            request.set_AclEntrys(ali_entrys)
            response = account["client"].do_action_with_exception(request)
            logger.info(f"{acl['account']} {acl['name']} add success => {acl_entrys}")
        except Exception as e:
            logger.error(f"{acl['account']} {acl['name']} add failed => {e}")
            return False

        return True


    def delete_acl_entrys(self, acl, acl_entrys):
        """
        给访问控制删除白名单列表
        """
        if not acl_entrys:
            return True

        ali_entrys = []
        for entry, comment in acl_entrys.items():
            acl_entry = {
                "entry": entry,
                "comment": comment
            }
            ali_entrys.append(acl_entry)
        ali_entrys = json.dumps(ali_entrys)


        try:
            account = self.aliyun_client.get_one_client(acl["account"])
            request = RemoveAccessControlListEntryRequest()
            request.set_accept_format('json')
            request.set_AclId(acl["id"])
            request.set_AclEntrys(ali_entrys)
            response = account["client"].do_action_with_exception(request)
            logger.info(f"{acl['account']} {acl['name']} remove success => {acl_entrys}")
        except Exception as e:
            logger.error(f"{acl['account']} {acl['name']} remove failed => {e}")
            return False

        return True


    def welab_delete_acl(self, acl_names, acl_entrys):
        """
        给访问控制名称列表，统一删除白名单
        """
        # 默认acl_names为空时，将所有的访问控制都加上白名单
        acls_info = self.get_all_acl_info()

        # acl_names不为空时，则按指定的访问控制来加白名单
        if acl_names:
            acls_tmp = [
                acl
                for acl in acls_info
                if acl["name"] in acl_names
            ]

            acls_info = acls_tmp

        for acl in acls_info:
            acl_on_entrys = [
                acl_entry["AclEntryIP"]
                for acl_entry in acl["acl_entrys"]
            ]
            # 访问控制中有的白名单则删除
            acl_del_entrys = {
                entry: comment
                for entry, comment in acl_entrys.items()
                if entry in acl_on_entrys
            }

            if acl_del_entrys:  self.delete_acl_entrys(acl, acl_del_entrys)

        return True



    def welab_add_acl(self, acl_names, acl_entrys):
        """
        给访问控制名称列表，统一加上白名单
        """
        # 默认acl_names为空时，将所有的访问控制都加上白名单
        acls_info = self.get_all_acl_info()

        # acl_names不为空时，则按指定的访问控制来加白名单
        if acl_names:
            acls_tmp = [
                acl
                for acl in acls_info
                if acl["name"] in acl_names
            ]

            acls_info = acls_tmp

        for acl in acls_info:
            acl_on_entrys = [
                acl_entry["AclEntryIP"]
                for acl_entry in acl["acl_entrys"]
            ]
            # 如果访问控制中没有则添加
            acl_add_entrys = {
                entry: comment
                for entry, comment in acl_entrys.items()
                if entry not in acl_on_entrys
            }

            if acl_add_entrys:
                self.add_acl_entrys(acl, acl_add_entrys)

        return True



    def get_all_acl(self, acl_names=None):
        acls_info = self.get_all_acl_info()
        if acl_names:
            acls_tmp = [
                acl
                for acl in acls_info
                if acl["name"] in acl_names
            ]
            acls_info = acls_tmp
        
        # 生成excel文件
        excel_writer = pd.ExcelWriter("slb的acl信息.xlsx")
        for acl_info in acls_info:
            df = pd.DataFrame(acl_info["acl_entrys"])
            df.to_excel(excel_writer, sheet_name=f"{acl_info['account']}-{acl_info['name']}", index=False)
        excel_writer.close()



    def sync_all_acl(self, acl_names=None):
        acls_info = self.get_all_acl_info()
        if acl_names:
            acls_tmp = [
                acl
                for acl in acls_info
                if acl["name"] in acl_names
            ]
            acls_info = acls_tmp

        # 转换下数据结构，方便后续数据比较
        acls_data = {}
        for acl in acls_info:
            acl["ips"] = [
                acl_entry["AclEntryIP"]
                for acl_entry in acl["acl_entrys"]
            ]
            acls_account = acls_data.get(acl["account"], {})
            acls_account[acl["name"]]   = acl
            acls_data[acl["account"]] = acls_account


        # 源账号和目的账号
        src_account = "weleb"
        dest_account = "welabconsfin"
        src_acls_info = acls_data[src_account]
        dest_acls_info = acls_data[dest_account]
        for name, src_acl in src_acls_info.items():
            if name not in dest_acls_info:
                logger.info(f"dest account:{dest_account} not have {src_acl['name']}")
                continue

            # 筛选出需要增加的白名单
            add_acl_entrys = {}
            dest_acl = dest_acls_info[name]
            for acl_entry in src_acl["acl_entrys"]:
                if acl_entry["AclEntryIP"] not in dest_acl["ips"]:
                    add_acl_entrys[acl_entry["AclEntryIP"]] = acl_entry.get("AclEntryComment", "暂时没有")

            # 开始同步
            if add_acl_entrys:
                self.add_acl_entrys(dest_acl, add_acl_entrys)
                # logger.info(f"{dest_acl['account']} {dest_acl['name']} add => {add_acl_entrys}")


    def tmp_check(self):
        use_infos = {
            "101.95.129.116/32":   "通联出口IP",
            "101.95.191.142/32":   "联通",
            "101.95.191.142/32":   "通联出口ip",
            "10.75.100.170/32":    "前海联通MSTP",
            "10.75.100.170/32":    "前海-联通MSTP30M",
            "10.86.18.6/32":       "",
            "10.86.18.6/32":       "阿里-移动",
            "112.95.78.30/32":     "深圳湾联通",
            "112.95.78.30/32":     "深圳湾 联通",
            "112.95.78.30/32":     "深圳湾-联通",
            "112.95.78.30/32":     "深圳湾联通线路",
            "112.95.78.31/32":     "深圳湾联通",
            "112.95.78.31/32":     "深圳湾 联通",
            "112.95.78.31/32":     "深圳湾联通线路",
            "112.95.78.32/32":     "深圳湾联通",
            "112.95.78.32/32":     "深圳湾 联通",
            "112.95.78.32/32":     "深圳湾联通线路",
            "113.108.62.154/32":   "优客",
            "113.108.62.154/32":   "优客电信互联网线路",
            "113.108.62.154/32":   "印尼团队深圳办公室",
            "113.108.62.155/32":   "优客电信互联网线路",
            "113.108.62.156/32":   "优客电信互联网线路",
            "113.108.62.157/32":   "优客电信互联网线路",
            "113.108.62.158/32":   "优客电信互联网线路",
            "113.108.62.242/32":   "优客电信互联网线路",
            "113.108.62.243/32":   "优客电信互联网线路",
            "113.108.62.244/32":   "优客电信互联网线路",
            "113.108.62.245/32":   "优客电信互联网线路",
            "113.108.62.246/32":   "优客电信互联网线路",
            "116.228.64.49/32":       "联通",
            "117.184.121.4/32":    "通联出口IP",
            "117.184.121.6/32":    "通联出口ip",
            "119.23.237.199/32":   "vpn-new01外网地址",
            "119.23.237.199/32":   "Welab-生产VPN",
            "119.29.172.14/32":    "深圳智信",
            "119.29.245.112/32":   "深圳智信",
            "119.29.98.244/32":    "深圳智信",
            "119.80.185.227/32":   "北京-创研办公室",
            "120.236.20.212/32":   "公司_基金小镇_移动",
            "120.236.20.212/32":   "基金小镇移动",
            "120.236.20.213/32":   "公司_基金小镇_移动",
            "120.236.20.213/32":   "基金小镇移动",
            "120.236.20.214/32":   "公司_基金小镇_移动",
            "120.236.20.214/32":   "基金小镇移动",
            "120.236.228.2/32":    "深圳湾移动城域网",
            "120.236.228.2/32":    "深圳湾-移动城域网",
            "120.236.228.2/32":    "移动城域网",
            "120.236.37.206/32":   "深圳市智富咨询服务有限公司",
            "120.76.196.17/32":    "生产_CDH_外网",
            "120.76.230.238/32":   "奕丰基金项目1",
            "120.76.230.238/32":   "理财产品需求，用于ifast-user服务访问短信平台接口。",
            "120.77.77.104/32":    "奕丰基金项目2",
            "120.77.84.81/32":     "yunwei-k8s-node91-爬虫",
            "120.78.143.51/32":    "阿里云生产环境-出口IP",
            "120.78.166.77/32":    "中国|0|广东省|深圳市|阿里云",
            "120.78.237.83/32":    "",
            "120.78.237.83/32":    "192.168网段的出口IP",
            "120.78.237.83/32":    "公司出口IP",
            "120.78.237.83/32":    "阿里云-生产环境第三方出口IP",
            "120.78.239.55/32":    "",
            "120.78.239.55/32":    "sa的出口ip",
            "120.78.239.55/32":    "sa账号业务出口ip",
            "120.78.239.55/32":    "sa账号出口ip",
            "120.78.239.55/32":    "Welab-工单系统-堡垒机-大数据集群",
            "120.78.239.55/32":    "中国|0|广东省|深圳市|阿里云",
            "120.78.239.55/32":    "生产_Harbor_外网",
            "120.78.239.55/32":    "阿里云-生产环境出口IP",
            "120.78.239.55/32":    "阿里云的出口IP",
            "120.78.70.28/32":     "奕丰基金项目3",
            "120.79.135.180/32":   "Welab-生产堡垒机",
            "120.79.195.113/32":   "welabconsfin安全出口IP",
            "121.201.87.16/32":    "",
            "121.201.87.36/32":    "Welab-天枢平台",
            "121.37.253.151/32":   "华为云测试openvpn公网ip",
            "123.118.130.157/32":  "北京办公区域",
            "123.207.82.175/32":   "深圳智信",
            "123.58.32.189/32":    "深圳湾互盟",
            "123.58.32.189/32":    "深圳湾 互盟",
            "123.58.32.189/32":    "深圳湾-第三方",
            "123.58.32.190/32":    "深湾互盟",
            "124.200.103.94/32":   "北京办公室_IP1",
            "124.71.17.34/32":     "华为云VPN出口IP",
            "134.175.57.177/32":   "深圳智信",
            "139.159.147.240/32":  "华为云_生产出口_IP",
            "139.159.147.240/32":  "天冕-业务应用-华为云出口IP1",
            "139.159.210.219/32":  "华为云远程桌面出口",
            "139.159.233.107/32":  "华为云tianmiantech-运维",
            "139.159.233.107/32":  "华为云ToB生产环境",
            "139.159.233.107/32":  "华为云_生产出口_IP",
            "139.159.238.6/32":    "华为云生产业务出口ip",
            "139.159.238.6/32":    "华为去ToB出口IP2",
            "139.159.238.6/32":    "天冕-业务应用-华为云出口IP2",
            "139.199.75.169/32":   "深圳智信",
            "139.9.79.190/32":     "华为云-测试-出口IP1",
            "139.9.79.190/32":     "华为测试",
            "139.9.79.190/32":     "华为测试fat出口ip",
            "139.9.95.41/32":      "华为云_CCE_测试_鲲鹏",
            "139.9.95.41/32":      "华为云_测试出口",
            "139.9.95.41/32":      "华为云_测试_出口",
            "140.206.57.164/32":   "通联出口IP",
            "140.206.57.166/32":   "通联出口ip",
            "140.207.168.194/32":  "联通",
            "175.154.206.14/32":   "联通",
            "183.237.197.206/32":  "前海移动",
            "183.238.107.237/32":  "基金小镇移动",
            "183.3.218.18/32":     "公司内网jumpserver",
            "183.3.218.18/32":     "深圳湾电信",
            "183.3.218.18/32":     "深圳湾-电信",
            "183.3.218.18/32":     "深圳湾电信线路",
            "202.104.125.234/32":  "阿里大厦T1",
            "202.105.131.130/32":  "公司_基金小镇_电信",
            "202.105.131.130/32":  "基金小镇",
            "202.105.131.130/32":  "基金小镇电信",
            "202.105.131.131/32":  "公司_基金小镇_电信",
            "202.105.131.131/32":  "基金小镇",
            "202.105.131.131/32":  "基金小镇电信",
            "202.105.131.132/32":  "公司_基金小镇_电信",
            "202.105.131.132/32":  "基金小镇",
            "202.105.131.132/32":  "基金小镇电信",
            "202.105.131.133/32":  "公司_基金小镇_电信",
            "202.105.131.133/32":  "基金小镇",
            "202.105.131.133/32":  "基金小镇电信",
            "202.105.131.134/32":  "公司_基金小镇_电信",
            "202.105.131.134/32":  "基金小镇",
            "202.105.131.134/32":  "基金小镇电信",
            "210.21.213.66/32":    "公司_基金小镇_联通",
            "210.21.213.66/32":    "前海联通",
            "210.21.213.67/32":    "公司_基金小镇_联通",
            "210.21.213.67/32":    "前海联通",
            "210.21.213.68/32":    "公司_基金小镇_联通",
            "210.21.213.68/32":    "前海联通",
            "210.21.213.69/32":    "公司_基金小镇_联通",
            "210.21.213.69/32":    "前海联通",
            "210.21.213.70/32":    "公司_基金小镇_联通",
            "210.21.213.70/32":    "前海联通",
            "210.21.213.71/32":    "公司_基金小镇_联通",
            "218.17.185.116/32":   "深圳市深弘君企业管理咨询有限公司",
            "218.17.194.100/32":   "中国|0|广东省|深圳市|电信",
            "218.17.194.101/32":   "",
            "218.17.194.101/32":   "中国|0|广东省|深圳市|电信",
            "218.17.194.102/32":   "中国|0|广东省|深圳市|电信",
            "218.17.194.99/32":    "中国|0|广东省|深圳市|电信",
            "218.17.90.138/32":    "公司_基金小镇_电信",
            "218.17.90.138/32":    "深圳湾电信",
            "218.17.90.139/32":    "公司_基金小镇_电信",
            "218.17.90.139/32":    "深圳湾电信",
            "218.17.90.140/32":    "公司_基金小镇_电信",
            "218.17.90.140/32":    "深圳湾电信",
            "218.17.90.141/32":    "公司_基金小镇_电信",
            "218.17.90.141/32":    "深圳湾电信",
            "218.17.90.142/32":    "公司_基金小镇_电信",
            "218.17.90.142/32":    "深圳湾电信",
            "218.17.90.238/32":    "中国|0|广东省|深圳市|电信",
            "218.18.137.66/32":    "前海电信",
            "218.18.137.66/32":    "基金小镇",
            "221.220.140.223/32":  "北京-在租办公室",
            "222.128.44.50/32":    "北京-办公室-联通固定IP",
            "222.72.143.68/32":    "通联出口ip",
            "222.72.143.69/32":    "通联出口ip",
            "47.112.185.48/32":    "深圳智信",
            "58.250.164.223/32":   "中国|0|广东省|深圳市|联通",
            "58.250.180.66/32":    "深圳湾联通",
            "58.250.180.66/32":    "深圳湾 联通",
            "58.250.180.67/32":    "深圳湾联通",
            "58.250.180.67/32":    "深圳湾 联通",
            "58.250.180.68/32":    "深圳湾联通",
            "58.250.180.68/32":    "深圳湾 联通",
            "58.250.180.69/32":    "深圳湾联通",
            "58.250.180.69/32":    "深圳湾 联通",
            "58.250.180.70/32":    "深圳湾联通",
            "58.250.180.70/32":    "深圳湾 联通",
        }
        
        use_ips = [
            "101.95.129.116/32",
            "113.108.62.155/32",
            "113.108.62.156/32",
            "113.108.62.157/32",
            "113.108.62.158/32",
            "113.108.62.242/32",
            "113.108.62.243/32",
            "113.108.62.244/32",
            "113.108.62.245/32",
            "113.108.62.246/32",
            "116.228.64.49/32",
            "117.184.121.4/32",
            "117.184.121.6/32",
            "119.29.172.14/32",
            "119.29.245.112/32",
            "119.29.98.244/32",
            "119.80.185.227/32",
            "120.236.37.206/32",
            "120.76.196.17/32",
            "120.77.77.104/32",
            "120.77.84.81/32",
            "120.78.143.51/32",
            "120.78.166.77/32",
            "120.78.70.28/32",
            "120.79.135.180/32",
            "120.79.195.113/32",
            "121.201.87.16/32",
            "121.201.87.36/32",
            "121.37.253.151/32",
            "123.118.130.157/32",
            "123.207.82.175/32",
            "123.58.32.190/32",
            "124.200.103.94/32",
            "124.71.17.34/32",
            "134.175.57.177/32",
            "139.159.210.219/32",
            "139.199.75.169/32",
            "140.206.57.164/32",
            "140.206.57.166/32",
            "140.207.168.194/32",
            "175.154.206.14/32",
            "183.237.197.206/32",
            "183.238.107.237/32",
            "202.104.125.234/32",
            "210.21.213.71/32",
            "218.17.185.116/32",
            "218.17.194.100/32",
            "218.17.194.102/32",
            "218.17.194.99/32",
            "218.17.90.238/32",
            "221.220.140.223/32",
            "222.128.44.50/32",
            "222.72.143.68/32",
            "222.72.143.69/32",
            "47.112.185.48/32",
            "58.250.164.223/32",
            "10.75.100.170/32",
            "10.86.18.6/32",
            "101.95.191.142/32",
            "119.23.237.199/32",
            "120.236.20.212/32",
            "120.236.20.213/32",
            "120.236.20.214/32",
            "120.76.230.238/32",
            "139.159.147.240/32",
            "210.21.213.66/32",
            "210.21.213.67/32",
            "210.21.213.68/32",
            "210.21.213.69/32",
            "210.21.213.70/32",
            "218.17.194.101/32",
            "218.17.90.138/32",
            "218.17.90.139/32",
            "218.17.90.140/32",
            "218.17.90.141/32",
            "218.17.90.142/32",
            "218.18.137.66/32",
            "58.250.180.66/32",
            "58.250.180.67/32",
            "58.250.180.68/32",
            "58.250.180.69/32",
            "58.250.180.70/32",
            "112.95.78.31/32",
            "112.95.78.32/32",
            "113.108.62.154/32",
            "120.236.228.2/32",
            "123.58.32.189/32",
            "139.159.233.107/32",
            "139.159.238.6/32",
            "139.9.79.190/32",
            "139.9.95.41/32",
            "202.105.131.130/32",
            "202.105.131.131/32",
            "202.105.131.132/32",
            "202.105.131.133/32",
            "202.105.131.134/32",
            "112.95.78.30/32",
            "120.78.237.83/32",
            "183.3.218.18/32",
            "120.78.239.55/32",
        ]
        new_ips = [
            "202.105.131.130/32",
            "202.105.131.131/32",
            "202.105.131.132/32",
            "202.105.131.133/32",
            "202.105.131.134/32",
            "218.17.90.138/32",
            "218.17.90.139/32",
            "218.17.90.140/32",
            "218.17.90.141/32",
            "218.17.90.142/32",
            "210.21.213.66/32",
            "210.21.213.67/32",
            "210.21.213.68/32",
            "210.21.213.69/32",
            "210.21.213.70/32",
            "113.108.62.154/32",
            "113.108.62.155/32",
            "113.108.62.156/32",
            "113.108.62.157/32",
            "113.108.62.158/32",
            "113.108.62.242/32",
            "113.108.62.243/32",
            "113.108.62.244/32",
            "113.108.62.245/32",
            "113.108.62.246/32"
        ]
        # for use_ip in use_ips:
        #     if use_ip not in new_ips:
        #         print(f"delete ip: {use_ip}    {use_infos[use_ip]}")
                
        for new_ip in new_ips:
            if new_ip not in use_ips:
                print(f"add ip: {new_ip}    {use_infos[new_ip]}")

def main():
    slb_acl = AliyunSlbAcl()
    # slb_acl.tmp_check()
    slb_acl.get_all_acl()

    # acl_names,是操作所有的访问控制
    # acl_names = [
        # '生产内网',
        # '阿里云内网',
    # ]

    # 所有的acl都增加
    # add_acl_entrys = {
    #     "120.236.20.213/32": "基金小镇移动",
    # }
    # slb_acl.welab_add_acl(acl_names, add_acl_entrys)

    # 所有的acl都删除
    # del_acl_entrys = {
    #     # "58.250.164.222/32": "绿创 联通",
    # }
    # slb_acl.welab_delete_acl(acl_names, del_acl_entrys)


    # slb_acl.sync_all_acl()

if __name__ == "__main__":
    main()